"use strict";

const hx = require("hbuilderx");
const os = require("os");
const path = require("path");
const fs = require("fs");
const childProcess = require("child_process");
const {
    version,
    id
} = require("./package.json");

// 全局进程
let spawnedProcess = null;
// 操作系统版本
const osVersion = os.version();
// 操作系统
const osName = os.platform();
// 插件id
const pluginId = id;
// 插件更新提示
const pluginUpdatePrompt = `${pluginId}.updatePrompt`;
// 插件更新时间
const pluginUpdatePromptTime = `${pluginId}.updatePromptTime`;
// 判断是否弹出气泡
let isPopUpWindow = false;

function activate(context) {

    // 检查插件更新

    checkUpgrade();

    const runCmd = hx.commands.registerCommand('hx-code-runner.run', async (uri) => {
        try {
            let filePath, languageId;

            // 如果从文件树右键调用，使用传入的uri
            if (uri && uri.fsPath) {
                filePath = uri.fsPath;
                languageId = path.extname(filePath).slice(1);
            } else {
                // 否则使用当前激活的编辑器
                const editor = await hx.window.getActiveTextEditor();
                if (!editor || !editor.document) {
                    hx.window.showErrorMessage('[hx-code-runner]没有活动的编辑器');
                    return;
                }
                filePath = editor.document.uri.fsPath;
                languageId = editor.document.languageId || path.extname(filePath).slice(1);
            }

            // 文件扩展名到语言ID的映射
            const extensionToLanguageMap = {
                // 脚本语言
                'js': 'javascript',
                'jsx': 'javascript',
                'mjs': 'javascript',
                'ts': 'typescript',
                'tsx': 'typescript',
                'py': 'python',
                'pyw': 'python',
                'php': 'php',
                'phtml': 'php',
                'rb': 'ruby',
                'go': 'go',
                'rs': 'rust',
                'kt': 'kotlin',
                'kts': 'kotlin',
                'swift': 'swift',
                'dart': 'dart',
                'lua': 'lua',
                'pl': 'perl',
                'pm': 'perl',
                'r': 'r',
                'scala': 'scala',
                'sc': 'scala',
                'groovy': 'groovy',
                'gvy': 'groovy',
                'gy': 'groovy',

                // 编译语言
                'c': 'c',
                'h': 'c',
                'cpp': 'cpp',
                'cc': 'cpp',
                'cxx': 'cpp',
                'c++': 'cpp',
                'hpp': 'cpp',
                'hxx': 'cpp',
                'java': 'java',
                'class': 'java',
                'cs': 'csharp',
                'csx': 'csharp',
                'vb': 'vb',
                'vbs': 'vb',

                // 标记语言
                'html': 'html',
                'htm': 'html',
                'xml': 'xml',
                'xhtml': 'html',
                'css': 'css',
                'scss': 'scss',
                'sass': 'sass',
                'less': 'less',
                'json': 'json',
                'yaml': 'yaml',
                'yml': 'yaml',
                'toml': 'toml',
                'ini': 'ini',
                'cfg': 'ini',
                'conf': 'ini',
                'md': 'markdown',
                'markdown': 'markdown',

                // 数据格式
                'sql': 'sql',
                'sh': 'shellscript',
                'bash': 'shellscript',
                'zsh': 'shellscript',
                'fish': 'shellscript',
                'ps1': 'powershell',
                'psm1': 'powershell',
                'psd1': 'powershell',
                'cmd': 'bat',
                'bat': 'bat',

                // 其他
                'dockerfile': 'dockerfile',
                'docker': 'dockerfile',
                'makefile': 'makefile',
                'mk': 'makefile',
                'cmake': 'cmake',
                'proto': 'protobuf',
                'graphql': 'graphql',
                'gql': 'graphql'
            };

            // 如果从文件扩展名获取的语言ID不在映射中，尝试映射
            if (extensionToLanguageMap[languageId]) {
                languageId = extensionToLanguageMap[languageId];
            }

            // console.log('原始语言ID:', path.extname(filePath).slice(1), '映射后语言ID:', languageId, '文件路径:', filePath);

            const commandToRun = resolveExecutor(languageId, filePath);
            if (!commandToRun) {
                hx.window.showErrorMessage('[hx-code-runner]没有找到 ' + languageId + ' 的执行器');
                return;
            }
            await runInShell(commandToRun, path.dirname(filePath), filePath);
        } catch (err) {
            hx.window.showErrorMessage(`[hx-code-runner] 发现错误，错位为${String(err)}`);
        }
    });

    const stopCmd = hx.commands.registerCommand('hx-code-runner.stop', async () => {
        stopRunningProcess();
    });

    const runCustomCmd = hx.commands.registerCommand('hx-code-runner.runCustom', async () => {
        const input = await hx.window.showInputBox({
            prompt: '[hx-code-runner]请输入自定义运行命令:'
        });
        if (!input) return;
        await runInShell(input, process.cwd());
    });

    const openConfigFileCmd = hx.commands.registerCommand('hx-code-runner.openConfigFile', async () => {
        await openConfigFile();
    });

    const selectExecutorPathCmd = hx.commands.registerCommand('hx-code-runner.selectExecutorPath', async () => {
        await selectExecutorPath();
    });

    const aboutCmd = hx.commands.registerCommand("hx-code-runner.about", async () => {
        about();
    });

    context.subscriptions.push(runCmd, stopCmd, runCustomCmd, openConfigFileCmd, selectExecutorPathCmd, aboutCmd);
}


function deactivate() {}

// 执行命令生成器
function resolveExecutor(languageId, filePath) {
    const hx = globalThis.hx || require('hbuilderx');

    // 从HBuilderX配置中获取可执行文件路径
    const getExecutorPath = (key, defaultValue) => {
        try {
            return hx.workspace.getConfiguration().get(`hx-code-runner.executorPath.${key}`) || defaultValue;
        } catch (e) {
            return defaultValue;
        }
    };

    // 从配置文件获取用户自定义配置
    const userMap = readUserExecutorMap();

    // 构建执行器映射
    const executorPaths = {
        node: getExecutorPath('node', 'node'),
        python: getExecutorPath('python', 'python'),
        java: getExecutorPath('java', 'java'),
        gcc: getExecutorPath('gcc', 'gcc'),
        gpp: getExecutorPath('gpp', 'g++'),
        go: getExecutorPath('go', 'go'),
        php: getExecutorPath('php', 'php'),
        ruby: getExecutorPath('ruby', 'ruby')
    };

    const defaultMap = {
        javascript: `${executorPaths.node} "$fullFileName"`,
        typescript: `${executorPaths.node} "$fullFileName"`,
        python: `${executorPaths.python} "$fullFileName"`,
        php: `${executorPaths.php} "$fullFileName"`,
        ruby: `${executorPaths.ruby} "$fullFileName"`,
        go: `${executorPaths.go} run "$fullFileName"`,
        java: `cd $dir && ${executorPaths.java}c $fileName && ${executorPaths.java} $fileNameWithoutExt`,
        c: `cd $dir && ${executorPaths.gcc} $fileName -o $fileNameWithoutExt && $dir$pathSep$fileNameWithoutExt`,
        cpp: `cd $dir && ${executorPaths.gpp} $fileName -o $fileNameWithoutExt && $dir$pathSep$fileNameWithoutExt`,
        bat: `chcp 65001 >nul && "$fullFileName"`
    };

    const map = Object.assign({}, defaultMap, userMap || {});
    const template = map[languageId];
    if (!template) return null;
    return interpolate(template, filePath);
}

// 模板字符串插值函数
function interpolate(template, filePath) {
    const dir = path.dirname(filePath);
    const fileName = path.basename(filePath);
    const fileNameWithoutExt = fileName.replace(/\.[^\.]+$/, '');
    const driveLetterMatch = /^([A-Za-z]):/.exec(filePath);
    const driveLetter = driveLetterMatch ? driveLetterMatch[1] : '';
    const vars = {
        '$workspaceRoot': dir,
        '$dir': dir,
        '$dirWithoutTrailingSlash': dir.replace(/[\\\/]$/, ''),
        '$fullFileName': filePath,
        '$fileName': fileName,
        '$fileNameWithoutExt': fileNameWithoutExt,
        '$driveLetter': driveLetter,
        '$pathSep': path.sep
    };
    let out = template;
    Object.keys(vars).forEach((k) => {
        out = out.split(k).join(vars[k]);
    });
    return out;
}

// 在shell 中运行代码
async function runInShell(command, cwd) {
    const hx = globalThis.hx || require('hbuilderx');
    stopRunningProcess();
    const shell = process.platform === 'win32' ? (process.env.COMSPEC || 'cmd.exe') : (process.env.SHELL ||
        '/bin/bash');
    const shellArgs = process.platform === 'win32' ? ['/d', '/s', '/c', command] : ['-lc', command];

    const channel = hx.window.createOutputChannel ? hx.window.createOutputChannel('Hx Code Runner') : null;
    if (channel) {
        channel.clear && channel.clear();
        channel.show && channel.show(true);

        // 美化的头部信息
        const timestamp = new Date().toLocaleTimeString();
        const header = `[hx-code-runner] - ${timestamp}`;
        const separator = '-'.repeat(50);
        const cwdLine = `工作目录: ${cwd}`;
        const cmdLine = `执行命令: ${command}`;

        channel.appendLine ? channel.appendLine(separator) : channel.append(separator + "\n");
        channel.appendLine ? channel.appendLine(header) : channel.append(header + "\n");
        channel.appendLine ? channel.appendLine(separator) : channel.append(separator + "\n");
        channel.appendLine ? channel.appendLine(cwdLine) : channel.append(cwdLine + "\n");
        channel.appendLine ? channel.appendLine(cmdLine) : channel.append(cmdLine + "\n");
        channel.appendLine ? channel.appendLine('─'.repeat(50)) : channel.append('─'.repeat(50) + "\n");
        channel.appendLine ? channel.appendLine('程序输出:') : channel.append('程序输出:\n');
    } else if (hx.window && typeof hx.window.appendOutput === 'function') {
        const timestamp = new Date().toLocaleTimeString();
        hx.window.appendOutput('[hx-code-runner] - ' + timestamp + os.EOL);
        hx.window.appendOutput('工作目录: ' + cwd + os.EOL);
        hx.window.appendOutput('执行命令: ' + command + os.EOL);
        hx.window.appendOutput('─'.repeat(50) + os.EOL);
        hx.window.appendOutput('程序输出:' + os.EOL);
    }

    spawnedProcess = childProcess.spawn(shell, shellArgs, {
        cwd,
        windowsVerbatimArguments: true
    });

    const writeOut = (data) => {
        const text = data.toString();
        if (channel) {
            // 保持原始输出格式，不添加额外装饰
            channel.appendLine ? channel.appendLine(text.replace(/\r?\n$/, '')) : channel.append(text);
        } else if (hx.window && typeof hx.window.appendOutput === 'function') {
            hx.window.appendOutput(text);
        } else {
            process.stdout.write(text);
        }
    };

    const writeErr = (data) => {
        const text = data.toString();
        if (channel) {
            // 错误输出用不同格式标识
            channel.appendLine ? channel.appendLine(`错误： ${text.replace(/\r?\n$/, '')}`) : channel.append(
                `错误： ${text}`);
        } else if (hx.window && typeof hx.window.appendOutput === 'function') {
            hx.window.appendOutput('错误：' + text);
        } else {
            process.stderr.write(text);
        }
    };

    spawnedProcess.stdout.on('data', writeOut);
    spawnedProcess.stderr.on('data', writeErr);
    spawnedProcess.on('exit', (code) => {
        if (channel) {
            const endTime = new Date().toLocaleTimeString();
            const status = code === 0 ? '执行成功' : `执行失败 (退出码: ${code})`;
            channel.appendLine ? channel.appendLine('─'.repeat(50)) : channel.append('─'.repeat(50) + "\n");
            channel.appendLine ? channel.appendLine(`${status} - ${endTime}`) : channel.append(
                `${status} - ${endTime}\n`);
            channel.appendLine ? channel.appendLine('═'.repeat(50)) : channel.append('═'.repeat(50) + "\n");
        }
        spawnedProcess = null;
    });
}

// 停止运行
function stopRunningProcess() {
    if (!spawnedProcess) return;
    try {
        if (process.platform === 'win32') {
            childProcess.exec('taskkill /pid ' + spawnedProcess.pid + ' /T /F');
        } else {
            process.kill(-spawnedProcess.pid);
        }
    } catch (e) {
        // ignore
        console.log(e);
    }
    spawnedProcess = null;
}


// 获取配置文件地址
function getSettingsPath() {

    // hbuilderx 应用程序 数据存放路径
    let appDataDir = hx.env.appData;

    // 在应用程序数据存放路径下找到 hx-code-runner的插件路径
    let configDir = path.join(appDataDir, 'hx-code-runner');

    // 检测文件夹是否存在
    let dirStatus = fs.existsSync(configDir);

    // 如果文件夹不存在则创建文件夹
    if (!dirStatus) {
        fs.mkdirSync(configDir, {
            recursive: true
        });
    }

    // 获取配置文件的路径
    return path.join(appDataDir, 'hx-code-runner', 'settings.json');


    // const home = os.homedir();
    // const dir = path.join(home, '.hbuilderx', 'hx-code-runner');
    // if (!fs.existsSync(dir)) {
    //     fs.mkdirSync(dir, {
    //         recursive: true
    //     });
    // }
    // return path.join(dir, 'settings.json');
}

// 确保配置文件生成
function ensureSettingsFile(settingsPath) {
    if (!fs.existsSync(settingsPath)) {
        const defaults = {
            "// 说明": "此配置文件用于添加更多编程语言的运行环境配置",
            "// 变量说明": {
                "$workspaceRoot": "工作区根目录",
                "$dir": "文件所在目录",
                "$fullFileName": "完整文件名（含路径）",
                "$fileName": "文件名（不含路径）",
                "$fileNameWithoutExt": "文件名（不含扩展名）",
                "$pathSep": "路径分隔符"
            },
            "executorMap": {
                "// 示例配置": "可以在此添加更多语言的执行器",
                "javascript": `node "$fullFileName"`,
                "typescript": `node "$fullFileName"`,
                "python": `python "$fullFileName"`,
                "php": `php "$fullFileName"`,
                "ruby": `ruby "$fullFileName"`,
                "go": `go run "$fullFileName"`,
                "java": `cd $dir && java $fileName && java $fileNameWithoutExt`,
                "c": `cd $dir && gcc $fileName -o $fileNameWithoutExt && $dir$pathSep$fileNameWithoutExt`,
                "cpp": `cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$pathSep$fileNameWithoutExt`,
                "bat": `chcp 65001 >nul && "$fullFileName"`,
                "rust": "rustc \"$fullFileName\" && $dir$pathSep$fileNameWithoutExt",
                "kotlin": "kotlinc \"$fullFileName\" -include-runtime -d $fileNameWithoutExt.jar && java -jar $fileNameWithoutExt.jar",
                "swift": "swift \"$fullFileName\"",
                "dart": "dart \"$fullFileName\"",
                "lua": "lua \"$fullFileName\"",
                "perl": "perl \"$fullFileName\"",
                "r": "Rscript \"$fullFileName\"",
                "scala": "scala \"$fullFileName\"",
                "groovy": "groovy \"$fullFileName\"",
                "shellscript": "bash \"$fullFileName\"",
                "powershell": "powershell -ExecutionPolicy ByPass -File \"$fullFileName\""
            }
        };
        fs.writeFileSync(settingsPath, JSON.stringify(defaults, null, 2), 'utf8');
    }
}

// 读取用户的执行路径
function readUserExecutorMap() {
    try {
        const settingsPath = getSettingsPath();
        if (!fs.existsSync(settingsPath)) return null;
        const content = fs.readFileSync(settingsPath, 'utf8');
        const json = JSON.parse(content);
        return json && json.executorMap ? json.executorMap : null;
    } catch (e) {
        return null;
    }
}

// 打开配置文件
async function openConfigFile() {
    const hx = globalThis.hx || require('hbuilderx');

    try {
        const settingsPath = getSettingsPath();
        ensureSettingsFile(settingsPath);
        await hx.workspace.openTextDocument(settingsPath).then(doc => hx.window.showTextDocument(doc));
        // hx.window.showInformationMessage('已打开配置文件，您可以在此添加更多语言的执行器配置');
    } catch (error) {
        hx.window.showErrorMessage('[hx-code-runner]打开配置文件失败: ' + error.message);
    }
}

// 选择执行路径
async function selectExecutorPath() {
    const hx = globalThis.hx || require('hbuilderx');

    try {
        // 显示语言选择列表
        const languages = [{
                label: 'Node.js',
                key: 'node',
                description: 'Node.js 解释器'
            },
            {
                label: 'Python',
                key: 'python',
                description: 'Python 解释器'
            },
            {
                label: 'Java',
                key: 'java',
                description: 'Java 运行时'
            },
            {
                label: 'GCC',
                key: 'gcc',
                description: 'C 编译器'
            },
            {
                label: 'G++',
                key: 'gpp',
                description: 'C++ 编译器'
            },
            {
                label: 'Go',
                key: 'go',
                description: 'Go 编译器'
            },
            {
                label: 'PHP',
                key: 'php',
                description: 'PHP 解释器'
            },
            {
                label: 'Ruby',
                key: 'ruby',
                description: 'Ruby 解释器'
            }
        ];

        const selectedLanguage = await hx.window.showQuickPick(languages, {
            placeHolder: '[hx-code-runner]请选择要配置的语言'
        });

        if (!selectedLanguage) return;

        // 打开文件选择对话框
        const fileUri = await hx.window.showOpenDialog({
            canSelectFiles: true,
            canSelectFolders: false,
            canSelectMany: false,
            filters: process.platform === 'win32' ? {
                '可执行文件': ['exe', 'bat', 'cmd']
            } : undefined,
            openLabel: '选择可执行文件'
        });

        if (!fileUri || fileUri.length === 0) return;

        const filePath = fileUri[0].fsPath;

        // 更新HBuilderX配置
        const config = hx.workspace.getConfiguration();
        const configKey = `hx-code-runner.executorPath.${selectedLanguage.key}`;
        await config.update(configKey, filePath, true);

        hx.window.showInformationMessage(`[hx-code-runner]已设置 ${selectedLanguage.label} 路径: ${filePath}`);

    } catch (error) {
        hx.window.showErrorMessage('[hx-code-runner]选择文件失败: ' + error.message);
    }
}

// 判断是否为JSON文件
function isJSON(str) {
    if (typeof str === "string") {
        try {
            // 解析json对象
            let obj = JSON.parse(str);
            if (typeof obj === "object" && obj) {
                return true;
            } else {
                return false;
            }
        } catch (e) {
            return false;
        }
    }
}

/**
 * @description 更新弹窗，点击【以后再说】，则本周内不再自动弹窗提示
 */
function showUpgradeBox(localVersion, marketPluginVersion) {
    if (marketPluginVersion == '' || marketPluginVersion == undefined) {
        return;
    };
    let lastChar = marketPluginVersion.charAt(marketPluginVersion.length - 1);
    let versionDescription = `【${pluginId}】发布 ${marketPluginVersion} 版本！`;
    // 弹窗信息
    let msg = versionDescription +
        `当前 ${localVersion} 版本。` +
        `<a href="https://ext.dcloud.net.cn/plugin?name=${pluginId}">更新日志</a>` +
        '<br/><br/>注意：更新后，重启HBuilderX才能生效。';

    // 弹窗的button的内容
    let btn = ['去插件市场更新', '以后再说'];

    hx.window.showInformationMessage(msg, btn).then((result) => {
        if (result === '去插件市场更新') {
            hx.env.openExternal(`https://ext.dcloud.net.cn/plugin?name=${pluginId}`);
        } else {
            let timeStamp = Math.round(new Date() / 1000) + 604800;
            let config = hx.workspace.getConfiguration();
            config.update(pluginUpdatePrompt, false).then(() => {
                config.update(pluginUpdatePromptTime, `${timeStamp}`);
            });
        }
    });
    isPopUpWindow = true;
}

/**
 * @description 关闭更新
 */
function noUpgrade(version) {
    let msg = `${pluginId}: 当前版本为 ${version}，是最新版本。`;
    let btns = ['关闭']

    let config = hx.workspace.getConfiguration();
    let updatePrompt = config.get(pluginUpdatePrompt);
    let updatePromptTime = config.get(pluginUpdatePromptTime);
    if (updatePromptTime != undefined || updatePrompt != undefined) {
        btns = ['有更新时提醒我', '关闭'];
    };

    hx.window.showInformationMessage(msg, btns).then((result) => {
        if (result === '有更新时提醒我') {
            config.update(pluginUpdatePrompt, true).then(() => {
                config.update(pluginUpdatePromptTime, '1577808001');
            });
        };
    });
};

/**
 * @description 获取线上版本号
 * @return {String} pluginVersion
 */
function getServerVersion() {
    let http = require('http');
    const versionUrl = 'http://update.dcloud.net.cn/hbuilderx/alpha/win32/plugins/index.json';
    return new Promise(function(resolve, reject) {
        http.get(versionUrl, (res) => {
            let data = "";
            res.on("data", (chunk) => {
                data += chunk;
            });
            res.on("end", () => {
                try {
                    let myPluginsVersion;
                    if (isJSON(data)) {
                        let allPlugins = JSON.parse(data);
                        let {
                            plugins
                        } = allPlugins;
                        for (let s of plugins) {
                            if (s.name == pluginId) {
                                myPluginsVersion = s.version;
                                break;
                            }
                        }
                    }
                    resolve(myPluginsVersion);
                } catch (e) {
                    reject('error');
                }
            });
            res.on("error", (e) => {
                reject('error');
                isPopUpWindow = true;
            });
        });
    })
}

/**
 * @description 自动检查更新
 */
async function checkUpgrade() {
    if (isPopUpWindow) {
        return;
    };

    // get week
    let currentTimeStamp = Math.round(new Date() / 1000);
    let config = await hx.workspace.getConfiguration();
    let updatePrompt = config.get(pluginUpdatePrompt);
    let updatePromptTime = config.get(pluginUpdatePromptTime);
    if (updatePromptTime) {
        try {
            if (updatePromptTime > currentTimeStamp) {
                return;
            }
        } catch (e) {

        };
    };
    let serverVersion = await getServerVersion();
    if (serverVersion != version) {
        showUpgradeBox(version, serverVersion);
    };
}

/**
 * @description 关于
 */
async function about() {
    let serverVersion = await getServerVersion();
    if ((serverVersion != version) && serverVersion != undefined) {
        showUpgradeBox(version, serverVersion);
    } else {
        noUpgrade(version);
    };
};


module.exports = {
    activate,
    deactivate
};
